package com.fau.wmbj.server.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.rmi.ServerException;

/**
 * request 预处理解析器
 *
 * @author jiawei
 * @version 1.0
 * @datetime 2022-09-28 15:35
 */

@Slf4j
public class RequestJsonHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    /**
     * key
     */
    private static final String JSON_BODY_KEY = "JSON_BODY_KEY";

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(RequestJson.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        // 获取jsonNode
        JsonNode jsonNode = getJsonBody(webRequest);

        RequestJson requestSingleParam = parameter.getParameterAnnotation(RequestJson.class);
        // 名
        assert requestSingleParam != null;
        String value = determineParamName(parameter, requestSingleParam);
        // 结果
        JsonNode paramValue = jsonNode.get(value);
        // 是否必须
        boolean require = requestSingleParam.require();
        if (paramValue == null && require) {
            throw new ServerException("parameter[" + value + "]不能为空。");
        }

        if (paramValue == null) {
            return null;
        }
        Class<?> parameterType = parameter.getParameterType();
        Constructor<?> constructor = parameterType.getConstructor(String.class);
        Object param = null;
        try {
            param = constructor.newInstance(paramValue.asText());
        } catch (Exception e) {
            log.error("bind method parameters error", e);
            throw new ServerException("parameter[" + value + "] format error for input value[" + paramValue.toString() + "]");
        }
        return param;
    }

    private String determineParamName(MethodParameter parameter, RequestJson requestSingleParam) {
        String value = requestSingleParam.value();
        if (value == null || value.isEmpty()) {
            value = parameter.getParameterName();
        }
        return value;
    }

    private JsonNode getJsonBody(NativeWebRequest webRequest) throws JsonProcessingException {

        // 有就直接获取
        String jsonBody = (String) webRequest.getAttribute(JSON_BODY_KEY, NativeWebRequest.SCOPE_REQUEST);
        // 没有就从请求中读取
        if (jsonBody == null) {
            try {
                HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
                RequestWrapper requestWrapper = new RequestWrapper(servletRequest);
                jsonBody = IOUtils.toString(requestWrapper.getReader());
                webRequest.setAttribute(JSON_BODY_KEY, jsonBody, NativeWebRequest.SCOPE_REQUEST);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return new ObjectMapper().readTree(jsonBody);
    }


}
